package top.v5it.japi.plus.data.jpa.service;

import org.springframework.data.domain.Example;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.transaction.annotation.Transactional;
import top.v5it.japi.plus.data.jpa.repository.JapiJpaRepository;

import java.util.List;
import java.util.Optional;

/**
 * 服务接口
 *
 * @author zhanpu
 * @date 2021/3/11
 */
public interface JapiJpaService<T, ID> {

    /**
     * 默认批量刷新上限数
     */
    int DEFAULT_BATCH_SIZE = 1000;

    /**
     * 获取{@link JapiJpaRepository}对象
     *
     * @return {@link JapiJpaRepository}对象
     */
    JapiJpaRepository<T, ID> getJapiRepository();

    /**
     * 新增或更新实体，存在则更新，否则新增
     *
     * @param entity 实体对象
     * @param <S>    实体类型
     * @return 实体对象
     */
    @Transactional(rollbackFor = Exception.class)
    default <S extends T> S addOrModify(S entity) {
        return getJapiRepository().save(entity);
    }

    /**
     * 新增或更新实体
     *
     * @param entity     实体对象
     * @param isOverride 是否更新原有数据，主要考虑在重合主键场景，当在不知情况下调用本方法，其本意是想新增实体，存在则新增失败或异常，结果可能导致数据库中已存在数据被覆盖重写
     * @param <S>        实体类型
     * @return 实体对象
     */
    @Transactional(rollbackFor = Exception.class)
    default <S extends T> S addOrModify(S entity, boolean isOverride) {
        return getJapiRepository().save(entity, isOverride);
    }

    /**
     * 根据id查找实体对象
     *
     * @param id 主键
     * @return 实体对象
     */
    default Optional<T> searchById(ID id) {
        return getJapiRepository().findById(id);
    }

    /**
     * 查找所有所有实体对象
     *
     * @return 实体对象
     */
    default List<T> searchAll() {
        return getJapiRepository().findAll();
    }

    /**
     * 根据id检验实体是否存在
     *
     * @param id 主键
     * @return true-存在，false-不存在
     */
    default boolean existsById(ID id) {
        return getJapiRepository().existsById(id);
    }

    /**
     * 根据id删除实体对象
     *
     * @param id 主键
     */
    @Transactional(rollbackFor = Exception.class)
    default void removeById(ID id) {
        getJapiRepository().deleteById(id);
    }

    /**
     * 动态更新对象，采用拼接hql语句，并绑定待更新字段，按id主键更新
     *
     * @param entity        实体对象
     * @param whereProperty 条件字段
     * @param <S>           实体类型
     * @return 实体对象
     */
    @Transactional(rollbackFor = Exception.class)
    default <S extends T> S dynamicModify(S entity, String... whereProperty) {
        return getJapiRepository().dynamicUpdate(entity, whereProperty);
    }

    /**
     * 批量新增，默认1000条刷新一次
     *
     * @param entities 实体对象
     * @param <S>      实体类型
     * @return 实体对象
     */
    @Transactional(rollbackFor = Exception.class)
    default <S extends T> List<S> addAllInBatch(Iterable<S> entities) {
        return this.addAllInBatch(entities, DEFAULT_BATCH_SIZE);
    }

    /**
     * 批量新增，按传入参数batchSize条刷新一次
     *
     * @param entities  实体对象
     * @param <S>       实体类型
     * @param batchSize 批次刷新数量
     * @return 实体对象
     */
    @Transactional(rollbackFor = Exception.class)
    default <S extends T> List<S> addAllInBatch(Iterable<S> entities, int batchSize) {
        return getJapiRepository().saveBatch(entities, batchSize);
    }

    /**
     * 批量新增，默认1000条刷新一次
     *
     * @param entities 实体对象
     * @param <S>      实体类型
     * @return 实体对象
     */
    @Transactional(rollbackFor = Exception.class)
    default <S extends T> List<S> modifyAllInBatch(Iterable<S> entities) {
        return this.modifyAllInBatch(entities, DEFAULT_BATCH_SIZE);
    }

    /**
     * 批量更新，按传入参数batchSize条刷新一次
     *
     * @param entities  实体对象
     * @param <S>       实体类型
     * @param batchSize 批次刷新数量
     * @return 实体对象
     */
    @Transactional(rollbackFor = Exception.class)
    default <S extends T> List<S> modifyAllInBatch(Iterable<S> entities, int batchSize) {
        return getJapiRepository().updateBatch(entities, batchSize);
    }

    /**
     * 根据实体对象删除
     *
     * @param entity 实体对象
     */
    @Transactional(rollbackFor = Exception.class)
    default void remove(T entity) {
        getJapiRepository().delete(entity);
    }

    /**
     * 删除所有实体对象
     *
     * @param entities 实体对象
     */
    @Transactional(rollbackFor = Exception.class)
    default void removeAllInBatch(Iterable<T> entities) {
        this.removeAllInBatch(entities, DEFAULT_BATCH_SIZE);
    }

    /**
     * 批量删除，按传入参数batchSize条刷新一次
     *
     * @param entities  实体对象
     * @param batchSize 批次刷新数量
     */
    @Transactional(rollbackFor = Exception.class)
    default void removeAllInBatch(Iterable<T> entities, int batchSize) {
        getJapiRepository().deleteBatch(entities, batchSize);
    }

    /**
     * 根据{@link Example}查找实体
     *
     * @param example 条件器
     * @param <S>     实体类型
     * @return 实体对象
     */
    default <S extends T> Optional<S> searchOne(Example<S> example) {
        return getJapiRepository().findOne(example);
    }

    /**
     * 根据{@link Example}查找所有符合的实体对象
     *
     * @param example 条件器
     * @param <S>     实体类型
     * @return 实体对象
     */
    default <S extends T> Iterable<S> searchAll(Example<S> example) {
        return getJapiRepository().findAll(example);
    }

    /**
     * 保存或更新：
     * <p>id不存在时，执行新增</p>
     * <p>
     * id存在时，执行动态更新，需要实体对象开启{@link org.hibernate.annotations.DynamicUpdate}注解：
     * 1、先根据id查询对象，无需开发者手动查询；2、执行动态更新
     * </p>
     *
     * @param entity 实体对象
     * @param <S>    实体类型
     * @return 实体对象
     */
    @Transactional(rollbackFor = Exception.class)
    default <S extends T> S addOrDynamicModify(S entity) {
        return getJapiRepository().saveOrDynamicUpdate(entity);
    }

    /**
     * 批量动态更新，按默认1000条提交一次，其内部调用{@link top.v5it.japi.plus.data.jpa.repository.support.JapiSimpleJpaRepository#dynamicUpdate}
     *
     * @param entities      实体对象
     * @param whereProperty 条件
     * @param <S>           实体类型
     * @return 实体对象
     */
    @Transactional(rollbackFor = Exception.class)
    default <S extends T> List<S> dynamicModifyBatch(Iterable<S> entities, String... whereProperty) {
        return this.dynamicModifyBatch(entities, DEFAULT_BATCH_SIZE, whereProperty);
    }

    /**
     * 批量动态更新，按传入参数batchSize条提交一次，其内部调用{@link top.v5it.japi.plus.data.jpa.repository.support.JapiSimpleJpaRepository#dynamicUpdate}
     *
     * @param entities      实体对象
     * @param <S>           实体类型
     * @param batchSize     批次刷新数量
     * @param whereProperty 条件
     * @return 实体对象
     */
    @Transactional(rollbackFor = Exception.class)
    default <S extends T> List<S> dynamicModifyBatch(Iterable<S> entities, int batchSize, String... whereProperty) {
        return getJapiRepository().dynamicUpdateBatch(entities, batchSize, whereProperty);
    }

    /**
     * 根据一批id批量查询，复合主键循环查询
     *
     * @param ids 主键集合
     * @return 实体对象
     */
    default List<T> searchAllById(Iterable<ID> ids) {
        return getJapiRepository().findAllById(ids);
    }

    /**
     * 获取实体，但不能调用实体的getter方法，可调用实体的setter方法
     *
     * @param id 主键
     * @return 实体对象
     */
    default T getOne(ID id) {
        return getJapiRepository().getOne(id);
    }

    /**
     * 分页查询
     *
     * @param example  条件器
     * @param pageable 分页对象
     * @param <S>      实体类型
     * @return 实体对象
     */
    default <S extends T> Page<S> searchAll(Example<S> example, Pageable pageable) {
        return getJapiRepository().findAll(example, pageable);
    }

    /**
     * 根据{@link Example}查找所有符合的实体对象，并排序
     *
     * @param example 条件器
     * @param sort    排序器
     * @param <S>     实体类型
     * @return 实体对象
     */
    default <S extends T> List<S> searchAll(Example<S> example, Sort sort) {
        return getJapiRepository().findAll(example, sort);
    }

    /**
     * 获取实体对象的总数量
     *
     * @return 总数量
     */
    default long count() {
        return getJapiRepository().count();
    }

    /**
     * 根据{@link Example}查找所有符合的实体对象数量
     *
     * @param example 条件器
     * @param <S>     实体类型
     * @return 总数量
     */
    default <S extends T> long count(Example<S> example) {
        return getJapiRepository().count(example);
    }

    /**
     * 根据{@link Example}查找实体对象是否存在
     *
     * @param example 条件器
     * @param <S>     实体类型
     * @return true-存在，false-不存在
     */
    default <S extends T> boolean exists(Example<S> example) {
        return getJapiRepository().exists(example);
    }

    /**
     * 删除实体对象
     *
     * @param entities 实体对象
     */
    @Transactional(rollbackFor = Exception.class)
    default void removeInBatch(Iterable<T> entities) {
        getJapiRepository().deleteInBatch(entities);
    }

}
